package cn.samler.diandi.api.service.impl;

import cn.samler.diandi.api.entity.Eval;
import cn.samler.diandi.api.entity.PostRequest;
import cn.samler.diandi.api.entity.PostRequestComment;
import cn.samler.diandi.api.entity.User;
import cn.samler.diandi.api.mapper.PostRequestMapper;
import cn.samler.diandi.api.mapper.UserEvalMapper;
import cn.samler.diandi.api.model.db.PostRequestOwner;
import cn.samler.diandi.api.model.io.Response;
import cn.samler.diandi.api.model.io.request.post.Evaluate;
import cn.samler.diandi.api.model.io.request.post.RequestMark;
import cn.samler.diandi.api.model.io.request.post.request.PostRequestCommentList;
import cn.samler.diandi.api.model.io.request.post.request.PostRequestDetail;
import cn.samler.diandi.api.model.io.request.post.request.PostRequestList;
import cn.samler.diandi.api.model.io.request.post.request.RequestStatus;
import cn.samler.diandi.api.model.io.request.pub.Page;
import cn.samler.diandi.api.model.io.request.user.UserRole;
import cn.samler.diandi.api.service.RequestService;
import cn.samler.diandi.api.service.UserRankService;
import cn.samler.diandi.api.utils.ContextHolderUtil;
import cn.samler.diandi.api.utils.ResponseBuilder;
import cn.samler.diandi.api.utils.SnowflakeIdUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Slf4j
public class RequestServiceImpl implements RequestService {
    private final PostRequestMapper postRequestMapper;
    private final UserEvalMapper userEvalMapper;
    private final UserRankService userRankService;

    public RequestServiceImpl(PostRequestMapper postRequestMapper, UserEvalMapper userEvalMapper, UserRankService userRankService) {
        this.postRequestMapper = postRequestMapper;
        this.userEvalMapper = userEvalMapper;
        this.userRankService = userRankService;
    }

    /**
     * 查询单个订单
     *
     * @param id request id
     * @return Response
     */
    @Override
    public Response detail(String id) {
        User user = (User) ContextHolderUtil.getCurrentUser();

        PostRequestOwner owner = getOwner(id, user.getId());
        if(owner == null)
            return new ResponseBuilder().failure().msg("你没有操作权限").build();

        // 匹配订单状态验证
        PostRequestDetail req = postRequestMapper.detailRequest(id);
        if(req == null)
            return new ResponseBuilder().failure().msg("订单不存在").build();

        return new ResponseBuilder().success().msg("获取成功").data(req).build();
    }

    /**
     * 列出需求订单列表
     *
     * @param form 表单
     * @return Response
     */
    @Override
    public Response list(PostRequestList form) {
        User user = (User) ContextHolderUtil.getCurrentUser();
        Page page = form.getPage();
        List<PostRequestDetail> list = postRequestMapper.listRequest(form.getSelf(), user.getId(),
                page.getStart(), page.getOffset());

        return new ResponseBuilder().success().msg("查询成功").data(list).build();
    }

    /**
     * 提交post申请
     * @param pid post id
     * @return Response
     */
    @Override
    public Response commit(Integer pid) {
        Integer postUid = postRequestMapper.getPostUid(pid);

        // 正常使用的场景下基本不会遇到这个问题，这里为了避免恶意申请
        if(postUid == null)
            return new ResponseBuilder().failure().msg("暂不接受新的申请").build();

        User user = (User) ContextHolderUtil.getCurrentUser();
        if(user.getId().equals(postUid))
            return new ResponseBuilder().failure().msg("不能申请自己发起的需求").build();

        if(postRequestMapper.getWaitCount(pid, user.getId()) > 0)
            return new ResponseBuilder().failure().msg("请耐心等待处理").build();

        SnowflakeIdUtil snowflakeIdUtil = SnowflakeIdUtil.getInstance();
        PostRequest postRequest = new PostRequest();
        postRequest.setId(String.format("%020d", snowflakeIdUtil.nextId()));
        postRequest.setPid(pid);
        postRequest.setOriginUid(user.getId());

        Integer commit = postRequestMapper.commit(postRequest);
        if(commit > 0)
            return new ResponseBuilder().success().msg("申请成功，请等待处理").build();

        return new ResponseBuilder().failure().msg("申请失败").build();
    }

    /**
     * 提交评价
     * @param form 评价表单
     * @return Response
     * @throws RuntimeException 评价失败回滚
     */
    @Override
    @Transactional
    public Response evaluate(Evaluate form) throws RuntimeException {
        User user = (User) ContextHolderUtil.getCurrentUser();

        // 权限验证 防止第三者恶意操作匹配订单
        PostRequestOwner owner = getOwner(form.getId(), user.getId());
        if(owner == null)
            return new ResponseBuilder().failure().msg("你没有操作权限").build();

        // 匹配订单状态验证
        PostRequestDetail req = postRequestMapper.detailRequest(form.getId());
        if(req == null)
            return new ResponseBuilder().failure().msg("订单不存在").build();
        if(req.getStatus() != RequestStatus.OVER)
            return new ResponseBuilder().failure().msg("订单状态异常").build();

        boolean isOrigin = user.getId().equals(req.getOriginUid());
        if(isOrigin && req.getOriginEvaluated() || !isOrigin && req.getTargetEvaluated())
            return new ResponseBuilder().failure().msg("你已经评价过了").build();

        Eval eval = new Eval();
        eval.setOrigin(user.getId());
        eval.setComment(form.getComment());
        eval.setRate(form.getRate());

        String role = postRequestMapper.getRole(form.getPid());
        if(isOrigin) {
            eval.setId(owner.getTargetUid());
            eval.setRole(role);
        } else {
            eval.setId(owner.getOriginUid());

            // 如果自己是需求发布者，评价时需要将role倒置
            if(UserRole.ROLE_SEEKER.equals(role))
                eval.setRole(UserRole.ROLE_EMPLOYER);
            else
                eval.setRole(UserRole.ROLE_SEEKER);
        }

        try {
            if(userEvalMapper.insert(eval) > 0) {
                if(postRequestMapper.updateEvaluate(form.getPid(), form.getId(), isOrigin) > 0) {
                    PostRequestComment comment = new PostRequestComment();
                    comment.setUid(user.getId());
                    comment.setRequestId(form.getId());
                    comment.setContent(String.format("我已提交对本次订单的评价：[rate:%d]", eval.getRate()));

                    // 提交评分
                    postRequestMapper.comment(comment);

                    // 提交评价留言
                    if(form.getComment().length() > 0) {
                        comment.setContent(form.getComment());
                        postRequestMapper.comment(comment);
                    }

                    // 异步计算并更新最新的评分均值
                    userRankService.update(eval);

                    return new ResponseBuilder().success().msg("评价成功").build();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("评价失败");
        }

        throw new RuntimeException("评价失败");
    }

    /**
     * 结束需求订单
     * @param form 结束表单
     * @return Response
     */
    @Override
    @Transactional
    public Response over(RequestMark form) {
        User user = (User) ContextHolderUtil.getCurrentUser();

        // 权限验证 防止第三者恶意操作匹配订单
        if(getOwner(form.getId(), user.getId()) == null)
            return new ResponseBuilder().failure().msg("你没有操作权限").build();

        // 匹配订单状态验证
        PostRequestDetail req = postRequestMapper.detailRequest(form.getId());
        if(req == null)
            return new ResponseBuilder().failure().msg("需求订单不存在").build();
        if(req.getStatus() != RequestStatus.PROCESSING)
            return new ResponseBuilder().failure().msg("当前状态无法结束订单").build();

        if(postRequestMapper.updateStatus(user.getId(), form, RequestStatus.OVER) >= 0) {
            PostRequestComment comment = new PostRequestComment();
            comment.setUid(user.getId());
            comment.setRequestId(form.getId());
            comment.setContent("我已结束本次需求订单。");

            postRequestMapper.comment(comment);

            return new ResponseBuilder().success().msg("需求订单已成功结束").build();
        }


        return new ResponseBuilder().failure().msg("操作失败").build();
    }

    /**
     * 发表评论
     * @param form 评论表单
     * @return Response
     */
    @Override
    public Response comment(PostRequestComment form) {
        User user = (User) ContextHolderUtil.getCurrentUser();

        if(getOwner(form.getRequestId(), user.getId()) == null)
            return new ResponseBuilder().failure().msg("你没有操作权限").build();

        form.setUid(user.getId());
        if(postRequestMapper.comment(form) > 0)
            return new ResponseBuilder().success().msg("评论成功").build();

        return new ResponseBuilder().failure().msg("评论失败").build();
    }

    /**
     * request评论列表
     *
     * @param form 分页表单
     * @return Response
     */
    @Override
    public Response listComment(PostRequestCommentList form) {
        User user = (User) ContextHolderUtil.getCurrentUser();
        if(getOwner(form.getRequestId(), user.getId()) == null)
            return new ResponseBuilder().failure().msg("你没有查看权限").build();

        Page page = form.getPage();
        List<PostRequestComment> list = postRequestMapper.listComment(form.getRequestId(), page.getStart(), page.getOffset());

        return new ResponseBuilder().success().msg("获取成功").data(list).build();
    }

    /**
     * 撤销request
     *
     * @param form Request标识实体
     * @return Response
     */
    @Override
    public Response cancel(RequestMark form) {
        User user = (User) ContextHolderUtil.getCurrentUser();
        PostRequestOwner owner = getOwner(form.getId(), user.getId());

        // 如果当前用户request的发起人，则没有权限撤销
        if(owner == null || !user.getId().equals(owner.getOriginUid()))
            return new ResponseBuilder().failure().msg("你没有操作权限").build();

        PostRequestDetail req = postRequestMapper.detailRequest(form.getId());
        if(req == null)
            return new ResponseBuilder().failure().msg("订单不存在").build();
        if(req.getStatus() != RequestStatus.WAIT_CONFIRM)
            return new ResponseBuilder().failure().msg("当前状态无法撤销").build();

        if(postRequestMapper.updateStatus(user.getId(), form, RequestStatus.CANCEL) > 0) {
            PostRequestComment comment = new PostRequestComment();
            comment.setUid(user.getId());
            comment.setRequestId(form.getId());
            comment.setContent("撤销了本次申请");

            postRequestMapper.comment(comment);

            return new ResponseBuilder().success().msg("撤销成功").build();
        }

        return new ResponseBuilder().failure().msg("撤销失败").build();
    }

    /**
     * 处理待处理的申请订单
     *
     * @param form Request标识实体
     * @param isAllow 是否同意
     * @return Response
     */
    @Override
    @Transactional
    public Response process(RequestMark form, boolean isAllow) {
        User user = (User) ContextHolderUtil.getCurrentUser();
        PostRequestOwner owner = getOwner(form.getId(), user.getId());

        // 如果当前用户非关联post的发起人，则没有权限同意或拒绝申请
        if(owner == null || !user.getId().equals(owner.getTargetUid()))
            return new ResponseBuilder().failure().msg("你没有操作权限").build();

        if(postRequestMapper.updateStatus(user.getId(), form, isAllow ? RequestStatus.PROCESSING : RequestStatus.REJECT) > 0) {
            PostRequestComment comment = new PostRequestComment();
            comment.setUid(user.getId());
            comment.setRequestId(form.getId());
            if(isAllow)
                comment.setContent("我同意了你的申请，记得准时报到哦！");
            else
                comment.setContent("很遗憾，本次申请没能通过，期待未来能有其它合作！");

            postRequestMapper.comment(comment);

            return new ResponseBuilder().success().msg("操作成功").build();
        }

        return new ResponseBuilder().failure().msg("操作失败").build();
    }

    /**
     * 用于判断登录的用户是否是对应request的拥有者
     * @param requestId request id
     * @param uid user id
     * @return PostRequestOwner or null
     */
    public PostRequestOwner getOwner(String requestId, int uid) {
        PostRequestOwner owner = postRequestMapper.getOwner(requestId);

        if(owner == null || owner.getOriginUid() != uid && owner.getTargetUid() != uid)
            return null;

        return owner;
    }
}
